package chen.videoedit;

import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Created by Cjiuxu on 2016/10/21.
 * 编辑video
 */
public class VideoAudioDispose {

    private final long AWAIT = 1000;
    private final String video;//视频文件路径
    private String audio;//音频文件路径
    private final String save_path;//处理后文件保存路径
    private final Context context;

    //视频文件
    private MediaExtractor VideoMediaExtractor;
    private int VideoTrack;
    private MediaFormat VideomediaFormat;
    //音频文件
    private MediaExtractor AudioMediaExtractor;
    private int AudioTrack;
    private MediaFormat AudiomediaFormat;
    private long timeLong;//时长
    //音频解码
    private MediaCodec AudioDecoderMediaCodec;
    private ByteBuffer[] AudioDecodeInput;
    private ByteBuffer[] AudioDecodeOutput;
    private MediaCodec.BufferInfo AudioInfo;

    //音频编码器
    private MediaCodec AudioEncoderMediaCodec;
    private ByteBuffer[] AudioEncoderInput;
    private ByteBuffer[] AudioEncoderOutput;
    private byte[] AudioAACbyte;//ACC数据处理
    private BufferedOutputStream bos;//aac文件写入
    //MP4合成
    private MediaMuxer mediaMuxer;
    private int videoTrackIndex;
    private int audioTrackIndex;

    private VideoAudioDispose(Context context, String video, String audio, String save_path) {
        this.video = video;
        this.audio = audio;
        this.save_path = save_path;
        this.context = context;
    }

    /**
     * 创建VideoAudioDispose
     */
    public static VideoAudioDispose newVideoAudioDispose(Context context, String video, String audio, String save_path) {
        return new VideoAudioDispose(context, video, audio, save_path);
    }

    /**
     * MP3转码ACC
     */
    public void MP3toAAC(String AAC_path) {
        if (TextUtils.isEmpty(AAC_path)) {
            AAC_path = getCachePath() + "/aac.aac";
        }
        try {
            FileOutputStream fos = new FileOutputStream(new File(AAC_path));//初始化文件
            bos = new BufferedOutputStream(fos, 20 * 1024);
        } catch (FileNotFoundException e) {
            Log.e("error", "FileOutputStream 初始化失败" + e.toString());
        }
        createOriginalAudioMediaExtractor(null);//创建音频文件MediaExtractor
        createAudioDecoderMediaCodec();//创建音频解码器
        createAudioEncoderMediaCodec();//创建音频编码器
        readAudioData();//读取数据
        audio = AAC_path;
    }

    /**
     * 合成音轨视轨
     */
    public void VideoADDAudio() throws Exception {
        createOriginalVideoMediaExtractor();//视频
        createOriginalAudioMediaExtractor(audio);//音频
        if (!isOriginalAudioFormat()) throw new Exception("音频不是AAC格式");//判断是否是AAC
        createMediaMuxer();//创建视频合成器
        isTimeLong();//视频时间判断
        muxerVideo();//写入视轨
        muxerAudio();//写入音轨
    }

    /**
     * 为MediaMuxer写入音频数据
     */
    private void muxerAudio() {
        //  mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 10);//设置帧
        AudioMediaExtractor.selectTrack(AudioTrack);//选定轨道
        int videoMaxInputSize = AudiomediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);//获取缓冲最大字节
        ByteBuffer inputBuffer = ByteBuffer.allocate(videoMaxInputSize);//分配缓冲
        MediaCodec.BufferInfo videoInfo = new MediaCodec.BufferInfo();//获取取出数据描述
        videoInfo.presentationTimeUs = 0;


        AudioMediaExtractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); //选择起点
        while (true) {
            int sampleSize = AudioMediaExtractor.readSampleData(inputBuffer, 0);
            if (sampleSize < 0) {
                AudioMediaExtractor.unselectTrack(AudioTrack);   //这里一定要释放选择的轨道，不然另一个轨道就无法选中了
                break;
            }
            long presentationTimeUs = AudioMediaExtractor.getSampleTime(); //获取当前时间戳
            if (presentationTimeUs > timeLong) {  //剪辑时间到了就跳出
                AudioMediaExtractor.unselectTrack(AudioTrack);
                break;
            }
            AudioMediaExtractor.advance();
            videoInfo.size = sampleSize;
            videoInfo.offset = 0;
            videoInfo.presentationTimeUs = presentationTimeUs;

            Log.e("jjj","videoInfo.presentationTimeUs="+videoInfo.presentationTimeUs+"   presentationTimeUs="+presentationTimeUs);

            mediaMuxer.writeSampleData(audioTrackIndex, inputBuffer, videoInfo);
        }
    }

    /**
     * 为MediaMuxer写入视频数据
     */
    private void muxerVideo() {

        VideoMediaExtractor.selectTrack(VideoTrack);//选定轨道
        int videoMaxInputSize = VideomediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);//获取缓冲最大字节
        int frame = VideomediaFormat.getInteger(MediaFormat.KEY_FRAME_RATE);//帧
        ByteBuffer inputBuffer = ByteBuffer.allocate(videoMaxInputSize);//分配缓冲
        MediaCodec.BufferInfo videoInfo = new MediaCodec.BufferInfo();//获取取出数据描述
        videoInfo.presentationTimeUs = 0;

        //获取源视频相邻帧之间的时间间隔。(1)
        long videoSampleTime = 1000 * 1000 / frame;
        VideoMediaExtractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); //选择起点
        while (true) {
            inputBuffer.clear();
            int sampleSize = VideoMediaExtractor.readSampleData(inputBuffer, 0);
            if (sampleSize < 0) {
                VideoMediaExtractor.unselectTrack(VideoTrack);   //这里一定要释放选择的轨道，不然另一个轨道就无法选中了
                break;
            }
            VideoMediaExtractor.advance();
            long presentationTimeUs = VideoMediaExtractor.getSampleTime(); //获取当前时间戳
            if (presentationTimeUs > timeLong) {  //剪辑时间到了就跳出
                VideoMediaExtractor.unselectTrack(VideoTrack);
                break;
            }
            int sampleFlag = VideoMediaExtractor.getSampleFlags();  //获取帧类型，只能识别是否为I帧(关键帧)
            videoInfo.offset = 0;
            if (sampleFlag == 1) videoInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME; //是否关键帧
            videoInfo.size = sampleSize;//数据长度
            videoInfo.presentationTimeUs += videoSampleTime;//
            Log.e("kkk"," videoInfo.presentationTimeUs="+ videoInfo.presentationTimeUs+"   presentationTimeUs="+presentationTimeUs);
            mediaMuxer.writeSampleData(videoTrackIndex, inputBuffer, videoInfo);
        }

    }

    /**
     * 初始化合成器
     */
    private void createMediaMuxer() {
        try {
            mediaMuxer = new MediaMuxer(save_path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        } catch (IOException e) {
            e.printStackTrace();
        }
        videoTrackIndex = mediaMuxer.addTrack(VideomediaFormat);//为封装器 添加视轨
        audioTrackIndex = mediaMuxer.addTrack(AudiomediaFormat);//为封装器 添加音轨
        mediaMuxer.start();
    }


    /**
     * 创建视频文件MediaExtractor
     */
    private void createOriginalVideoMediaExtractor() {
        if (VideoMediaExtractor != null) {
            VideoMediaExtractor.release();
            VideoMediaExtractor = null;
        }
        VideoMediaExtractor = new MediaExtractor();//初始化分类器
        try {
            VideoMediaExtractor.setDataSource(video);
        } catch (IOException e) {
            Log.e("error", "MediaExtractor音频初始化失败" + e.toString());
        }
        int Acount = VideoMediaExtractor.getTrackCount();
        for (int i = 0; i < Acount; i++) {
            MediaFormat format = VideoMediaExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("video/")) {
                VideoTrack = i;//视频
                VideomediaFormat = format;
            }
        }
    }


    /**
     * 创建音频文件MediaExtractor
     *
     * @param aacSave
     */
    private void createOriginalAudioMediaExtractor(String aacSave) {
        if (AudioMediaExtractor != null) {
            AudioMediaExtractor.release();
            AudioMediaExtractor = null;
        }
        AudioMediaExtractor = new MediaExtractor();//初始化分类器
        try {
            if (TextUtils.isEmpty(aacSave)) {
                AudioMediaExtractor.setDataSource(audio);
            } else {
                AudioMediaExtractor.setDataSource(aacSave);
            }
        } catch (IOException e) {
            Log.e("error", "MediaExtractor音频初始化失败" + e.toString());
        }
        int Acount = AudioMediaExtractor.getTrackCount();
        for (int i = 0; i < Acount; i++) {
            MediaFormat format = AudioMediaExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("audio/")) {
                AudioTrack = i;//音轨
                AudiomediaFormat = format;//音轨格式

            }
        }
    }


    /**
     * 读取音频文件数据放入解码器
     */
    private void readAudioData() {
        AudioMediaExtractor.selectTrack(AudioTrack);//音频部分
        AudioMediaExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);//从头开始解析
        while (true) {
            int inputBufferIndex = AudioDecoderMediaCodec.dequeueInputBuffer(-1);//获取
            if (inputBufferIndex < 0) break;
            ByteBuffer buffer = AudioDecodeInput[inputBufferIndex];
            buffer.clear();
            int sampleSize = AudioMediaExtractor.readSampleData(buffer, 0);//读取数据放入解码器
            if (sampleSize < 0) break;//sampleSize读取完成
            AudioDecoderMediaCodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, 0, 0);//通知解码器数据放入
            AudioMediaExtractor.advance();//读取下移动

            int outputBufferIndex = AudioDecoderMediaCodec.dequeueOutputBuffer(AudioInfo, AWAIT);//获取解码数据

            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = AudioDecodeOutput[outputBufferIndex];//获取解析数据
                writeAudioData(outputBuffer);//数据放入编码器
                outputBuffer.clear();
                AudioDecoderMediaCodec.releaseOutputBuffer(outputBufferIndex, false);//此操作一定要做，不然MediaCodec用完所有的Buffer后 将不能向外输出数据
                outputBufferIndex = AudioDecoderMediaCodec.dequeueOutputBuffer(AudioInfo, AWAIT);
            }
        }
    }

    /**
     * 写入音频文件数据放入编码器得到AAC编码数据
     *
     * @param buffer
     */
    private void writeAudioData(ByteBuffer buffer) {
        int inputIndex = AudioEncoderMediaCodec.dequeueInputBuffer(-1);//
        if (inputIndex < 0) return;
        ByteBuffer inputBuffer = AudioEncoderInput[inputIndex];
        inputBuffer.put(buffer);//PCM数据填充给inputBuffer
        AudioEncoderMediaCodec.queueInputBuffer(inputIndex, 0, AudioInfo.size, 0, 0);//通知编码器 编码

        int out_index = AudioEncoderMediaCodec.dequeueOutputBuffer(AudioInfo, AWAIT);//获取解码数据
        while (out_index >= 0) {
            ByteBuffer outputBuffer = AudioEncoderOutput[out_index];//获取编码数据
            int L = AudioInfo.size + 7;//给adts头字段空出7的字节
            if (AudioAACbyte == null || AudioAACbyte.length < L) {
                AudioAACbyte = new byte[L];
            }
            addADTStoPacket(AudioAACbyte, L);
            outputBuffer.get(AudioAACbyte, 7, AudioInfo.size);//音频数据放入
            try {
                bos.write(AudioAACbyte, 0, L);
                Log.e("lll", "转码");
            } catch (IOException e) {
                e.printStackTrace();
            }
            AudioEncoderMediaCodec.releaseOutputBuffer(out_index, false);//此操作一定要做，不然MediaCodec用完所有的Buffer后 将不能向外输出数据
            out_index = AudioEncoderMediaCodec.dequeueOutputBuffer(AudioInfo, AWAIT);
        }
    }


    /**
     * 创建音频编码器
     */
    private void createAudioEncoderMediaCodec() {

        try {
            MediaFormat encodeFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 44100, 2);//参数对应-> mime type、采样率、声道数
            encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);//比特率
            encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 10 * 1024);

            AudioEncoderMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
            AudioEncoderMediaCodec.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        } catch (IOException e) {
            Log.e("error", "MediaCodec 音频编码器错误" + e.toString());
        }
        AudioEncoderMediaCodec.start();
        AudioEncoderInput = AudioEncoderMediaCodec.getInputBuffers();
        AudioEncoderOutput = AudioEncoderMediaCodec.getOutputBuffers();

    }

    /**
     * 创建音频解码器
     */
    private void createAudioDecoderMediaCodec() {

        try {
            AudioDecoderMediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_MPEG);
            AudioDecoderMediaCodec.configure(AudiomediaFormat, null, null, 0);
        } catch (IOException e) {
            Log.e("error", "MediaCodec 音频解码器错误" + e.toString());
        }
        AudioDecoderMediaCodec.start();//启动MediaCodec ，等待传入数据
        AudioDecodeInput = AudioDecoderMediaCodec.getInputBuffers();//MediaCodec在此ByteBuffer[]中获取输入数据
        AudioDecodeOutput = AudioDecoderMediaCodec.getOutputBuffers();//MediaCodec将解码后的数据放到此ByteBuffer[]中 我们可以直接在这里面得到PCM数据
        AudioInfo = new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息
    }

    /**
     * 释放资源
     */
    public void release() {
        if (VideoMediaExtractor != null) {
            VideoMediaExtractor.release();
            VideoMediaExtractor = null;
        }
        if (AudioMediaExtractor != null) {
            AudioMediaExtractor.release();
            AudioMediaExtractor = null;
        }
        if (AudioDecoderMediaCodec != null) {
            AudioDecoderMediaCodec.stop();
            AudioDecoderMediaCodec.release();
            AudioDecoderMediaCodec = null;
        }
        if (AudioEncoderMediaCodec != null) {
            AudioEncoderMediaCodec.stop();
            AudioEncoderMediaCodec.release();
            AudioEncoderMediaCodec = null;
        }
        if (mediaMuxer != null) {
            mediaMuxer.stop();
            mediaMuxer.release();
            mediaMuxer = null;
        }
    }


    /**
     * 判断音频格式是否AAC
     */
    private Boolean isOriginalAudioFormat() {
        String mime = AudiomediaFormat.getString(MediaFormat.KEY_MIME);
        Boolean is;
        if (TextUtils.equals(mime, MediaFormat.MIMETYPE_AUDIO_AAC)) {//判断音频格式是否AAC
            is = true;
        } else {
            is = false;
        }
        return is;
    }


    /**
     * 判断音频视频长度
     */
    public void isTimeLong() {
        long vt = VideomediaFormat.getLong(MediaFormat.KEY_DURATION);
        long at = AudiomediaFormat.getLong(MediaFormat.KEY_DURATION);
        if (vt >= at) {
            timeLong = at;
        } else {
            timeLong = vt;
        }
    }

    /**
     * AAC添加ADTS头
     */
    private void addADTStoPacket(byte[] packet, int packetLen) {
        int profile = 2; // AAC LC
        int freqIdx = 4; // 44.1KHz
        int chanCfg = 2; // CPE
        packet[0] = (byte) 0xFF;
        packet[1] = (byte) 0xF9;
        packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
        packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
        packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
        packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
        packet[6] = (byte) 0xFC;
    }

    /**
     * 获取缓存路径
     */
    @NonNull
    private String getCachePath() {
        return context.getExternalCacheDir().getPath();
    }


}
